﻿using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace SZHelperCore
{
    /// <summary>
    /// 定时器任务管理器，用于管理所有定时器任务的添加、移除和获取操作。
    /// </summary>
    public class TimerTaskManager
    {
        /// <summary>
        /// 存储所有定时器任务的线程安全字典，键为任务名称，值为定时器任务服务实例。
        /// </summary>
        private static readonly ConcurrentDictionary<string, TimerTaskService> taskList = new();

        /// <summary>
        /// 添加一个定时器任务到任务列表中。如果任务名称已存在，则更新对应的任务。
        /// </summary>
        /// <param name="name">定时器任务的名称。</param>
        /// <param name="service">定时器任务服务实例。</param>
        public static void AddTimerTask(string name, TimerTaskService service)
        {
            taskList.AddOrUpdate(name, service, (_, _) => service);
        }

        /// <summary>
        /// 从任务列表中移除指定名称的定时器任务。
        /// </summary>
        /// <param name="name">要移除的定时器任务的名称。</param>
        public static void RemoveTimerTask(string name)
        {
            taskList.TryRemove(name, out _);
        }

        /// <summary>
        /// 获取所有定时器任务的线程安全字典。
        /// </summary>
        /// <returns>包含所有定时器任务的线程安全字典。</returns>
        public static ConcurrentDictionary<string, TimerTaskService> GetAll()
        {
            return taskList;
        }

        /// <summary>
        /// 根据任务名称获取对应的定时器任务服务实例。
        /// </summary>
        /// <param name="name">要获取的定时器任务的名称。</param>
        /// <returns>如果找到对应的任务，则返回该任务的服务实例；否则返回 null。</returns>
        public static TimerTaskService GetTimerTask(string name)
        {
            taskList.TryGetValue(name, out var task);
            return task;
        }
    }

    /// <summary>
    /// 定时器任务服务类，用于管理单个定时器任务的启动、停止和执行。
    /// </summary>
    public class TimerTaskService : IDisposable
    {
        /// <summary>
        /// 定时器任务的配置信息。
        /// </summary>
        private ScheduledTaskModel timerInfo;
        /// <summary>
        /// 无参数的定时器任务委托。
        /// </summary>
        private Action TimerTaskDelegateFun = null;
        /// <summary>
        /// 带参数的定时器任务委托。
        /// </summary>
        private Action<object> ParmTimerTaskDelegateFun = null;
        /// <summary>
        /// 传递给带参数定时器任务委托的参数。
        /// </summary>
        private object[] parm = null;
        /// <summary>
        /// 执行定时器任务的异步任务。
        /// </summary>
        private Task TaskService;
        /// <summary>
        /// 用于取消定时器任务的取消令牌源。
        /// </summary>
        private CancellationTokenSource ct = new();
        /// <summary>
        /// 指示定时器任务是否正在运行。
        /// </summary>
        private bool isStart = false;
        /// <summary>
        /// 轮询间隔时间，默认为 1 毫秒。
        /// </summary>
        private TimeSpan _timerSpanMil = new TimeSpan(0, 0, 0, 0, 1);
        /// <summary>
        /// 距离下一次任务执行的时间间隔。
        /// </summary>
        private TimeSpan timeSpan = new TimeSpan(100);
        /// <summary>
        /// 下一次任务执行的时间。
        /// </summary>
        private DateTime nextRunTime;
        /// <summary>
        /// 上一次任务执行的时间。
        /// </summary>
        private DateTime lastRunTime;

        /// <summary>
        /// 获取定时器任务是否正在运行。
        /// </summary>
        public bool IsStart => isStart;
        /// <summary>
        /// 获取或设置定时器任务的名称。
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 获取下一次任务执行的时间。
        /// </summary>
        public DateTime NextRunTime => nextRunTime;
        /// <summary>
        /// 获取上一次任务执行的时间。
        /// </summary>
        public DateTime LastRunTime => lastRunTime;

        public ScheduledTaskModel TimerInfo { get => timerInfo; set => timerInfo = value; }

        /// <summary>
        /// 初始化 TimerTaskService 类的新实例。
        /// </summary>
        /// <param name="_timer">定时器任务的配置信息。</param>
        /// <exception cref="ArgumentNullException">当 _timer 为 null 时抛出。</exception>
        private TimerTaskService(ScheduledTaskModel _timer)
        {
            TimerInfo = _timer ?? throw new ArgumentNullException(nameof(_timer));
        }

        /// <summary>
        /// 初始化 TimerTaskService 类的新实例，使用无参数的任务委托。
        /// </summary>
        /// <param name="_timer">定时器任务的配置信息。</param>
        /// <param name="trd">无参数的定时器任务委托。</param>
        private TimerTaskService(ScheduledTaskModel _timer, Action trd) : this(_timer)
        {
            TimerTaskDelegateFun = trd;
        }

        /// <summary>
        /// 初始化 TimerTaskService 类的新实例，使用带参数的任务委托。
        /// </summary>
        /// <param name="_timer">定时器任务的配置信息。</param>
        /// <param name="ptrd">带参数的定时器任务委托。</param>
        private TimerTaskService(ScheduledTaskModel _timer, Action<object> ptrd) : this(_timer)
        {
            ParmTimerTaskDelegateFun = ptrd;
        }

        /// <summary>
        /// 设置带参数定时器任务委托的参数。
        /// </summary>
        /// <param name="_parm">要设置的参数。</param>
        private void SetParm(params object[] objects)
        {
            parm = objects;
        }

        /// <summary>
        /// 手动触发一次定时器任务的执行。
        /// </summary>
        public void ManualRun()
        {
            RunStart();
        }

        /// <summary>
        /// 启动定时器任务。
        /// </summary>
        public void Start()
        {
            // 修复：添加任务状态检查，避免重复启动任务
            if (TaskService?.Status == TaskStatus.Created)
            {
                TaskService.Start();
            }
        }

        /// <summary>
        /// 停止定时器任务。
        /// </summary>
        public void Stop()
        {
            isStart = false;
            ct.Cancel();
            // 修复：添加超时处理，避免无限等待
            TaskService?.Wait(TimeSpan.FromSeconds(5));
        }

        /// <summary>
        /// 开始执行一次定时器任务。
        /// </summary>
        /// <returns>表示任务执行的异步任务，如果没有有效的任务委托则返回 null。</returns>
        private Task RunStart()
        {
            Task task = null;
            try
            {
                if (TimerTaskDelegateFun != null)
                {
                    task = new Task(TimerTaskDelegateFun);
                }
                else if (ParmTimerTaskDelegateFun != null)
                {
                    task = new Task(ParmTimerTaskDelegateFun, parm);
                }
                lastRunTime = DateTime.Now;
                task?.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error starting task: {ex.Message}");
            }
            return task;
        }

        /// <summary>
        /// 执行主任务逻辑。
        /// </summary>
        /// <returns>如果任务执行后需要停止定时器，则返回 true；否则返回 false。</returns>
        private bool RunMain()
        {
            var task = RunStart();
            if (task == null) return false;

            try
            {
                if (TimerInfo.TimerType == TimerType.LoopStop)
                {
                    // 修复：添加超时处理，避免无限等待
                    task.Wait(TimeSpan.FromSeconds(5));
                }
                else if (TimerInfo.TimerType == TimerType.DesDate)
                {
                    // 修复：添加超时处理，避免无限等待
                    task.Wait(TimeSpan.FromSeconds(5));
                    return true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error waiting for task: {ex.Message}");
            }
            return false;
        }

        /// <summary>
        /// 定时器任务的主循环方法。
        /// </summary>
        /// <param name="ct">用于取消任务的取消令牌源。</param>
        private void Run(CancellationTokenSource ct)
        {
            if (!IsStart)
            {
                isStart = true;
                getNextRunTime();
            }

            while (isStart)
            {
                if (DateTime.Now < NextRunTime)
                {
                    // 修复：使用异步等待，避免阻塞线程
                    Task.Delay(_timerSpanMil, ct.Token).Wait(ct.Token);
                    if (ct.IsCancellationRequested) break;
                    continue;
                }

                if (ct.IsCancellationRequested) break;

                if (RunMain())
                {
                    break;
                }

                getNextRunTime();
            }
        }

        /// <summary>
        /// 计算下一次任务执行的时间。
        /// </summary>
        private void getNextRunTime()
        {
            DateTime now = DateTime.Now;

            switch (TimerInfo.TimerType)
            {
                case TimerType.DesDate:
                    nextRunTime = new DateTime(TimerInfo.Year, TimerInfo.Month, TimerInfo.Day,
                        TimerInfo.Hour, TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond);
                    timeSpan = nextRunTime - now;
                    break;

                case TimerType.EveryYear:
                    nextRunTime = new DateTime(now.Year, TimerInfo.Month, TimerInfo.Day,
                        TimerInfo.Hour, TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddYears(1);
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EveryMonth:
                    nextRunTime = new DateTime(now.Year, now.Month, TimerInfo.Day,
                        TimerInfo.Hour, TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddMonths(1);
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EveryDay:
                    nextRunTime = new DateTime(now.Year, now.Month, now.Day,
                        TimerInfo.Hour, TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddDays(1);
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EveryWeek:
                    int targetDayOfWeek = TimerInfo.Week;
                    // 修复：添加范围检查，确保 targetDayOfWeek 在有效范围内
                    if (targetDayOfWeek < 0 || targetDayOfWeek > 6)
                    {
                        throw new ArgumentOutOfRangeException(nameof(TimerInfo.Week), "Week must be between 0 and 6");
                    }
                    DateTime nextDay = now.Date;
                    int diff = targetDayOfWeek - (int)now.DayOfWeek;
                    if (diff <= 0) diff += 7;
                    nextRunTime = now.Date.AddDays(diff).Add(new TimeSpan(
                        TimerInfo.Hour, TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond / 1000));
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EveryHour:
                    nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour,
                        TimerInfo.Minute, TimerInfo.Second, TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddHours(1);
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EveryMinute:
                    nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
                        TimerInfo.Second, TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddMinutes(1);
                    timeSpan = NextRunTime - now;
                    break;

                case TimerType.EverySecond:
                    nextRunTime = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second,
                        TimerInfo.Millisecond);
                    while ((NextRunTime - now) < _timerSpanMil)
                        nextRunTime = NextRunTime.AddSeconds(1);
                    timeSpan = NextRunTime - now;
                    break;

                default:
                    nextRunTime = now.AddYears(TimerInfo.Year).AddMonths(TimerInfo.Month)
                        .AddDays(TimerInfo.Day).AddHours(TimerInfo.Hour)
                        .AddMinutes(TimerInfo.Minute).AddSeconds(TimerInfo.Second)
                        .AddMilliseconds(TimerInfo.Millisecond);
                    timeSpan = NextRunTime - now;
                    break;
            }
        }

        /// <summary>
        /// 创建一个新的定时器任务服务实例，使用无参数的任务委托。
        /// </summary>
        /// <param name="info">定时器任务的配置信息。</param>
        /// <param name="_trd">无参数的定时器任务委托。</param>
        /// <param name="name">定时器任务的名称，可选参数。</param>
        /// <returns>新创建的定时器任务服务实例。</returns>
        public static TimerTaskService CreateTimerTaskService(ScheduledTaskModel info, Action _trd, string name = null, params object[] objects)
        {
            var tus = new TimerTaskService(info, _trd);
            tus.TaskService = new Task(() => tus.Run(tus.ct));
            tus.Name = name;
            if (name != null) TimerTaskManager.AddTimerTask(name, tus);
            return tus;
        }

        /// <summary>
        /// 创建一个新的定时器任务服务实例，使用带参数的任务委托。
        /// </summary>
        /// <param name="info">定时器任务的配置信息。</param>
        /// <param name="_ptrd">带参数的定时器任务委托。</param>
        /// <param name="parm">传递给带参数定时器任务委托的参数。</param>
        /// <param name="name">定时器任务的名称，可选参数。</param>
        /// <returns>新创建的定时器任务服务实例。</returns>
        public static TimerTaskService CreateTimerTaskService(ScheduledTaskModel info, Action<object> _ptrd,string name = null, params object[] objects)
        {
            var tus = new TimerTaskService(info, _ptrd);
            tus.SetParm(objects);
            tus.TaskService = new Task(() => tus.Run(tus.ct));
            tus.Name = name;
            if (name != null) TimerTaskManager.AddTimerTask(name, tus);
            return tus;
        }

        /// <summary>
        /// 释放 TimerTaskService 类的当前实例所使用的资源。
        /// </summary>
        public void Dispose()
        {
            isStart = false;
            ct.Cancel();
            // 修复：添加超时处理，避免无限等待
            TaskService?.Wait(TimeSpan.FromSeconds(5));
            ct.Dispose();
            // 避免重复调用 Dispose
            GC.SuppressFinalize(this);
        }
    }

    /// <summary>
    /// 定时器任务的配置信息类。
    /// </summary>
    public class ScheduledTaskModel
    {
        /// <summary>
        /// 定时器名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 类名称
        /// </summary>
        public string ClassName { get; set; }
        /// <summary>
        /// 获取或设置定时器任务的类型。
        /// </summary>
        public TimerType TimerType { get; set; }
        /// <summary>
        /// 获取或设置年份相关的配置信息。
        /// </summary>
        public int Year { get; set; }
        /// <summary>
        /// 获取或设置月份相关的配置信息。
        /// </summary>
        public int Month { get; set; }
        /// <summary>
        /// 获取或设置星期相关的配置信息，范围 0 - 6（星期日 - 星期六）。
        /// </summary>
        public int Week { get; set; }
        /// <summary>
        /// 获取或设置日期相关的配置信息。
        /// </summary>
        public int Day { get; set; }
        /// <summary>
        /// 获取或设置小时相关的配置信息。
        /// </summary>
        public int Hour { get; set; }
        /// <summary>
        /// 获取或设置分钟相关的配置信息。
        /// </summary>
        public int Minute { get; set; }
        /// <summary>
        /// 获取或设置秒相关的配置信息。
        /// </summary>
        public int Second { get; set; }
        /// <summary>
        /// 获取或设置毫秒相关的配置信息。
        /// </summary>
        public int Millisecond { get; set; }
        public string Description { get; set; }
        public string Arguments { get; set; }
    }

    /// <summary>
    /// 定时器任务的类型枚举。
    /// </summary>
    public enum TimerType
    {
        /// <summary>
        /// 每秒执行一次。
        /// </summary>
        EverySecond,
        /// <summary>
        /// 每分钟执行一次。
        /// </summary>
        EveryMinute,
        /// <summary>
        /// 每小时执行一次。
        /// </summary>
        EveryHour,
        /// <summary>
        /// 每天执行一次。
        /// </summary>
        EveryDay,
        /// <summary>
        /// 每周执行一次。
        /// </summary>
        EveryWeek,
        /// <summary>
        /// 每月执行一次。
        /// </summary>
        EveryMonth,
        /// <summary>
        /// 每年执行一次。
        /// </summary>
        EveryYear,
        /// <summary>
        /// 在指定日期执行一次。
        /// </summary>
        DesDate,
        /// <summary>
        /// 循环执行直到手动停止。
        /// </summary>
        LoopStop,
        /// <summary>
        /// 该枚举值未在代码中使用，可能为预留类型。
        /// </summary>
        LoopStart,
    }
}